
#include "stdafx.h"
#include <qfile.h>
#include <qfileinfo.h>

#include "pf_graphic.h"

#include "pf_debug.h"
#include "pf_fileio.h"
#include "pf_math.h"
#include "pf_units.h"
#include "pf_settings.h"
#include "pf_layer.h"
#include "pf_block.h"


/**
 * Default constructor.
 */
PF_Graphic::PF_Graphic(PF_Container* parent)
        : PF_Document(parent),
        layerList(),
blockList(true)

{

    PF_SETTINGS->beginGroup("/Defaults");
    setUnit(PF_Units::stringToUnit(PF_SETTINGS->readEntry("/Unit", "None")));
    PF_SETTINGS->endGroup();

    LX_Define::Unit unit = getUnit();

    if (unit==LX_Define::Inch) {
        addVariable("$DIMASZ", 0.1, 40);
        addVariable("$DIMEXE", 0.05, 40);
        addVariable("$DIMEXO", 0.025, 40);
        addVariable("$DIMGAP", 0.025, 40);
        addVariable("$DIMTXT", 0.1, 40);
    } else {
        addVariable("$DIMASZ",
                    PF_Units::convert(2.5, LX_Define::Millimeter, unit), 40);
        addVariable("$DIMEXE",
                    PF_Units::convert(1.25, LX_Define::Millimeter, unit), 40);
        addVariable("$DIMEXO",
                    PF_Units::convert(0.625, LX_Define::Millimeter, unit), 40);
        addVariable("$DIMGAP",
                    PF_Units::convert(0.625, LX_Define::Millimeter, unit), 40);
        addVariable("$DIMTXT",
                    PF_Units::convert(2.5, LX_Define::Millimeter, unit), 40);
    }
	setModified(false);
}



/**
 * Destructor.
 */
PF_Graphic::~PF_Graphic() {}



/**
 * Counts the entities on the given layer.
 */
unsigned long int PF_Graphic::countLayerEntities(PF_Layer* layer) {

    int c=0;

    if (layer!=NULL) {
        for (PF_Entity* t=firstEntity(LX_Define::ResolveNone);
                t!=NULL;
                t=nextEntity(LX_Define::ResolveNone)) {

            if (t->getLayer()!=NULL &&
                    t->getLayer()->getName()==layer->getName()) {
                c+=t->countDeep();
            }
        }
    }

    return c;
}



/**
 * Removes the given layer and undoes all entities on it.
 */
void PF_Graphic::removeLayer(PF_Layer* layer) {

    if (layer!=NULL && layer->getName()!="0") {

        // remove all entities on that layer:
        startUndoCycle();
        for (PF_Entity* e=firstEntity(LX_Define::ResolveNone);
                e!=NULL;
                e=nextEntity(LX_Define::ResolveNone)) {

            if (e->getLayer()!=NULL &&
                    e->getLayer()->getName()==layer->getName()) {

                e->setUndoState(true);
                e->setLayer("0");
                addUndoable(e);
            }
        }
        endUndoCycle();

        // remove all entities in blocks that are on that layer:
        for (int bi=0; bi<blockList.count(); bi++) {
            PF_Block* blk = blockList.at(bi);

            if (blk!=NULL) {
                for (PF_Entity* e=blk->firstEntity(LX_Define::ResolveNone);
                        e!=NULL;
                        e=blk->nextEntity(LX_Define::ResolveNone)) {

                    if (e->getLayer()!=NULL &&
                            e->getLayer()->getName()==layer->getName()) {

                        e->setUndoState(true);
                        e->setLayer("0");
                        //addUndoable(e);
                    }
                }
            }
        }

        layerList.remove(layer);
    }
}


/**
 * Clears all layers, blocks and entities of this graphic.
 * A default layer (0) is created.
 */
void PF_Graphic::newDoc() {

    PF_DEBUG->print("PF_Graphic::newDoc");

    clear();

    clearLayers();
    clearBlocks();

    addLayer(new PF_Layer("0"));
    //addLayer(new PF_Layer("ByBlock"));

	setModified(false);
}



/**
 * Saves this graphic with the current filename and settings.
 */
bool PF_Graphic::save(bool isAutoSave) {

	bool ret = false;

	
    PF_DEBUG->print("PF_Graphic::save");
	if (isAutoSave && !isModified()) {
	    PF_DEBUG->print("  autsave and not modified => not saved");
		ret = true;
	} else {
                const QString *actualName;
		LX_Define::FormatType actualType;

		actualType = formatType;
		if (isAutoSave) {
			actualName = new QString(autosaveFilename);
			if (formatType == LX_Define::FormatUnknown) {
				actualType = LX_Define::FormatDXF;
			}
		} else {
			actualName = new QString(filename);
		}
            PF_DEBUG->print("  file: %s", actualName->toLatin1().data());
		PF_DEBUG->print("  format: %d", (int)actualType);
		PF_DEBUG->print("  export...");
		ret = PF_FILEIO->fileExport(*this, *actualName, actualType);
		delete actualName;

		if (ret && !isAutoSave) {
		    setModified(false);
			layerList.setModified(false);
			blockList.setModified(false);
			// Remove old autosave file
			QFile f(autosaveFilename);
			if (f.exists()) {
				PF_DEBUG->print("  removing old autosave file %s",
                                                        autosaveFilename.toLatin1().data());
				f.remove();
			}
		}
	}

    PF_DEBUG->print("PF_Graphic::save ok");

    return ret;
}



/**
 * Saves this graphic with the given filename and current settings.
 */
bool PF_Graphic::saveAs(const QString &filename, LX_Define::FormatType type) {

    PF_DEBUG->print("PF_Graphic::saveAs");

    this->filename = filename;
        QString *oldAutosaveName = new QString(autosaveFilename);
	QFileInfo finfo(filename);
	// Construct new autosave filename by prepending # to the filename
	// part, using the same directory as the destination file.
        this->autosaveFilename = finfo.path() + "/#" + finfo.fileName();
	this->formatType = type;

    bool ret = save();

	if (ret) {
		// save was successful, remove old autosave file
		QFile f(*oldAutosaveName);
		if (f.exists()) {
			PF_DEBUG->print("removing old autosave file %s",
                                                oldAutosaveName->toLatin1().data());
			f.remove();
		}
	}

	delete oldAutosaveName;

	return ret;
}



/**
 * Loads the given fils into this graphic.
 */
bool PF_Graphic::open(const QString &filename, LX_Define::FormatType type) {
    PF_DEBUG->print("PF_Graphic::open(%s)", filename.toLatin1().data());

	bool ret = false;

    this->filename = filename;
	QFileInfo finfo(filename);
	// Construct new autosave filename by prepending # to the filename
	// part, using the same directory as the destination file.
        this->autosaveFilename = finfo.path() + "/#" + finfo.fileName();

    // clean all:
    newDoc();

    // import file:
    ret = PF_FILEIO->fileImport(*this, filename, type);
		
	setModified(false);
	layerList.setModified(false);
	blockList.setModified(false);

    //cout << *((PF_Graphic*)graphic);
    //calculateBorders();
	
    PF_DEBUG->print("PF_Graphic::open(%s): OK", filename.toLatin1().data());

    return ret;
}



/**
 * @return true if the grid is switched on (visible).
 */
bool PF_Graphic::isGridOn() {
	int on = getVariableInt("$GRIDMODE", 1);
	return on!=0;
}



/**
 * Enables / disables the grid.
 */
void PF_Graphic::setGridOn(bool on) {
	addVariable("$GRIDMODE", (int)on, 70);
}



/**
 * Sets the unit of this graphic to 'u'
 */
void PF_Graphic::setUnit(LX_Define::Unit u) {

    setPaperSize(PF_Units::convert(getPaperSize(), getUnit(), u));

    addVariable("$INSUNITS", (int)u, 70);

    //unit = u;
}



/**
 * Gets the unit of this graphic 
 */
LX_Define::Unit PF_Graphic::getUnit() {
    return (LX_Define::Unit)getVariableInt("$INSUNITS", 0);
    //return unit;
}



/**
 * @return The linear format type for this document.
 * This is determined by the variable "$LUNITS".
 */
LX_Define::LinearFormat PF_Graphic::getLinearFormat() {
    int lunits = getVariableInt("$LUNITS", 2);

    switch (lunits) {
    default:
    case 2:
        return LX_Define::Decimal;
        break;

    case 1:
        return LX_Define::Scientific;
        break;

    case 3:
        return LX_Define::Engineering;
        break;

    case 4:
        return LX_Define::Architectural;
        break;

    case 5:
        return LX_Define::Fractional;
        break;
    }

    return LX_Define::Decimal;
}



/**
 * @return The linear precision for this document.
 * This is determined by the variable "$LUPREC".
 */
int PF_Graphic::getLinearPrecision() {
    return getVariableInt("$LUPREC", 4);
}



/**
 * @return The angle format type for this document.
 * This is determined by the variable "$AUNITS".
 */
LX_Define::AngleFormat PF_Graphic::getAngleFormat() {
    int aunits = getVariableInt("$AUNITS", 0);

    switch (aunits) {
    default:
    case 0:
        return LX_Define::DegreesDecimal;
        break;

    case 1:
        return LX_Define::DegreesMinutesSeconds;
        break;

    case 2:
        return LX_Define::Gradians;
        break;

    case 3:
        return LX_Define::Radians;
        break;

    case 4:
        return LX_Define::Surveyors;
        break;
    }

    return LX_Define::DegreesDecimal;
}



/**
 * @return The linear precision for this document.
 * This is determined by the variable "$LUPREC".
 */
int PF_Graphic::getAnglePrecision() {
    return getVariableInt("$AUPREC", 4);
}



/**
 * @return The insertion point of the drawing into the paper space.
 * This is the distance from the lower left paper edge to the zero
 * point of the drawing. DXF: $PINSBASE.
 */
PF_Vector PF_Graphic::getPaperInsertionBase() {
    return getVariableVector("$PINSBASE", PF_Vector(0.0,0.0));
}


/**
 * Sets the PINSBASE variable.
 */
void PF_Graphic::setPaperInsertionBase(const PF_Vector& p) {
    addVariable("$PINSBASE", p, 10);
}


/**
 * @return Paper size in graphic units.
 */
PF_Vector PF_Graphic::getPaperSize() {
    PF_Vector def = PF_Units::convert(PF_Vector(210.0,297.0),
                                      LX_Define::Millimeter, getUnit());

    PF_Vector v1 = getVariableVector("$PLIMMIN", PF_Vector(0.0,0.0));
    PF_Vector v2 = getVariableVector("$PLIMMAX", def);

    return v2-v1;
}


/**
 * Sets a new paper size.
 */
void PF_Graphic::setPaperSize(const PF_Vector& s) {
    addVariable("$PLIMMIN", PF_Vector(0.0,0.0), 10);
    addVariable("$PLIMMAX", s, 10);
}



/**
 * @return Paper format.
 * This is determined by the variables "$PLIMMIN" and "$PLIMMAX".
 *
 * @param landscape will be set to true for landscape and false for portrait if not NULL.
 */
LX_Define::PaperFormat PF_Graphic::getPaperFormat(bool* landscape) {
    PF_Vector size = PF_Units::convert(getPaperSize(),
                                       getUnit(), LX_Define::Millimeter);

    if (landscape!=NULL) {
        *landscape = (size.x>size.y);
    }

    return PF_Units::paperSizeToFormat(size);
}



/**
 * Sets the paper format to the given format.
 */
void PF_Graphic::setPaperFormat(LX_Define::PaperFormat f, bool landscape) {
    PF_Vector size = PF_Units::paperFormatToSize(f);

    if (landscape) {
        double tmp = size.x;
        size.x = size.y;
        size.y = tmp;
    }

	if (f!=LX_Define::Custom) {
    	setPaperSize(PF_Units::convert(size, LX_Define::Millimeter, getUnit()));
	}
}



/**
 * @return Paper space scaling (DXF: $PSVPSCALE).
 */
double PF_Graphic::getPaperScale() {
    double ret;

    ret = getVariableDouble("$PSVPSCALE", 1.0);
    if (ret<1.0e-6) {
        ret = 1.0;
    }

    return ret;
}



/**
 * Sets a new scale factor for the paper space.
 */
void PF_Graphic::setPaperScale(double s) {
    addVariable("$PSVPSCALE", s, 40);
}



/**
 * Centers drawing on page. Affects DXF variable $PINSBASE.
 */
void PF_Graphic::centerToPage() {
    PF_Vector size = getPaperSize();

    double scale = getPaperScale();

    PF_Vector pinsbase = (size-getSize()*scale)/2.0 - getMin()*scale;

    setPaperInsertionBase(pinsbase);
}



/**
 * Fits drawing on page. Affects DXF variable $PINSBASE.
 */
void PF_Graphic::fitToPage() {
	double border = PF_Units::convert(25.0, LX_Define::Millimeter, getUnit());
	PF_Vector ps = getPaperSize() - PF_Vector(border, border);
	PF_Vector s = getSize();
	double fx = LX_DOUBLEMAX;
	double fy = LX_DOUBLEMAX;
	//double factor = 1.0;

	//ps = PF_Units::convert(ps, getUnit(), LX_Define::Millimeter);

	if (fabs(s.x)>1.0e-6) {
		fx = ps.x / s.x;
	}
	if (fabs(s.y)>1.0e-6) {
		fy = ps.y / s.y;
	}

	setPaperScale(std::min(fx, fy));
	centerToPage();
}



/**
 * Dumps the entities to stdout.
 */
std::ostream& operator << (std::ostream& os, PF_Graphic& g) {
    os << "--- Graphic: \n";
    os << "---" << *g.getLayerList() << "\n";
    os << "---" << *g.getBlockList() << "\n";
    os << "---" << (PF_Undo&)g << "\n";
    os << "---" << (PF_Container&)g << "\n";

    return os;
}

